﻿using System;
using System.Web.UI;
using System.Web.UI.WebControls;


namespace BreadCrumbs
{
	/// <summary>
	/// Web control that displays a breadcrumb.
	/// </summary>
	public class Breadcrumb : WebControl
	{
		private BreadcrumbPath _path;


		#region ctor

		/// <summary>
		/// Initializes a new instance of the <see cref="Breadcrumb"/> class.
		/// </summary>
		public Breadcrumb()
		{
			_path = new BreadcrumbPath(this);
		}

		#endregion


		#region Properties

		/// <summary>
		/// Gets the breadcrumb path.
		/// </summary>
		/// <value>The breadcrumb path.</value>
		public BreadcrumbPath Path
		{
			get
			{
				return _path;
			}
		}

        /// <summary>
        /// Gets the sole breadcrumb instance for the given page.
        /// </summary>
        /// <param name="page">The page.</param>
        /// <returns>If a breadcrumb instance is found, returns that breadcrumb instance; otherwise returns null</returns>
        /// <exception cref="ArgumentNullException">page is null</exception>
        public static Breadcrumb GetCurrent(System.Web.UI.Page page)
        {
            if (page == null)
                throw new ArgumentNullException("page");

            return (Breadcrumb)page.Items[typeof(Breadcrumb)];
        }

		#endregion


		#region Rendering


        /// <summary>
        /// Renders the HTML opening tag of the control to the specified writer. This method is used primarily by control developers.
        /// </summary>
        /// <param name="writer">A <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
		public override void RenderBeginTag(HtmlTextWriter writer)
		{
			AddAttributesToRender(writer);
			writer.RenderBeginTag(HtmlTextWriterTag.Div);
		}


		/// <summary>
		/// Renders the contents of the control to the specified writer. This method is used primarily by control developers.
		/// </summary>
		/// <param name="writer">A <see cref="HtmlTextWriter"/> that represents the output stream to render HTML 
		/// content on the client.</param>
		protected override void RenderContents(HtmlTextWriter writer)
		{
			bool renderSeparator = false;

			foreach (var level in _path.Levels)
			{
				if (!renderSeparator)
					renderSeparator = true;
				else
					writer.Write("&nbsp;&gt;&nbsp;");

				if (String.IsNullOrEmpty(level.Value))
				{
					writer.RenderBeginTag(HtmlTextWriterTag.Span);
					writer.WriteEncodedText(level.Key);
					writer.RenderEndTag();
				}
				else
				{
					writer.AddAttribute(HtmlTextWriterAttribute.Href, ResolveClientUrl(level.Value));
					writer.RenderBeginTag(HtmlTextWriterTag.A);
					writer.WriteEncodedText(level.Key);
					writer.RenderEndTag();
				}
			}
		}

		#endregion


		#region Save & load viewstate

		/// <summary>
		/// Saves any state that was modified after the <see cref="M:System.Web.UI.WebControls.Style.TrackViewState"/> method was invoked.
		/// </summary>
		/// <returns>An object that contains the current view state of the control; otherwise, if there is no
		/// view state associated with the control, null.</returns>
		protected override object SaveViewState()
		{
			var baseState = base.SaveViewState();

			// create pairs
			Pair[] pairs = null;
			if (_path.Levels.Count > 0)
			{
				pairs = new Pair[_path.Levels.Count];

				int index = 0;
				foreach(var level in _path.Levels)
				{
					pairs[index++] = new Pair(level.Key, level.Value);
				}

			}

			if ((baseState != null) || (pairs != null))
				return new Pair(baseState, pairs);

			return null;
		}


		/// <summary>
		/// Restores view-state information from a previous request that was saved with 
		/// the <see cref="M:System.Web.UI.WebControls.WebControl.SaveViewState"/> method.
		/// </summary>
		/// <param name="savedState">An object that represents the control state to restore.</param>
		protected override void LoadViewState(object savedState)
		{
			if (savedState == null)
				return;

			// saved state is pair
			Pair pair = (Pair)savedState;

			// allow base to load state
			base.LoadViewState(pair.First);

			if (pair.Second != null)
			{
				Pair[] levelPairs = (Pair[])pair.Second;

				foreach (var resourcePair in levelPairs)
				{
					string title = (string)resourcePair.First;
					string path  = (string)resourcePair.Second ?? "";
					_path.Levels.Add(title, path);
				}
			}
		}

		#endregion
	}
}
